package mylittleinvestigations;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import javax.imageio.ImageIO;
import mylittleinvestigations.CaseInformation.Case;
import mylittleinvestigations.CaseInformation.CaseParseException;
import mylittleinvestigations.CaseInformation.CaseXmlWriter;
import mylittleinvestigations.Collisions.HitBox;
import org.newdawn.slick.geom.Polygon;

public class Crowd
{
    private static int crowdCount = 0;
    
    private Vector2 position;
    private String name;
    private int frameDuration;
    private ArrayList<Character> characterList;
    private String videoId;
    private Video video;
    
    public Crowd(Vector2 position, String name, int frameDuration)
    {
        this.position = position;
        this.name = name;
        this.frameDuration = frameDuration;
        this.characterList = new ArrayList<Character>();
        this.anchorPosition = -1;
        this.interactionLocation = new Vector2(-1, -1);
        this.interactFromAnywhere = false;
        this.videoId = "Crowd" + (++crowdCount);
        this.video = null;
    }
    
    private HitBox hitBox;
    
    public HitBox getHitBox() { return this.hitBox; }
    public void setHitBox(HitBox hitBox) { this.hitBox = hitBox; }
    
    private int anchorPosition;
    
    public int getAnchorPosition() { return this.anchorPosition; }
    public void setAnchorPosition(int anchorPosition) { this.anchorPosition = anchorPosition; }
    
    private Polygon clickPolygon;
    
    public Polygon getClickPolygon() { return this.clickPolygon; }
    public void setClickPolygon(Polygon clickPolygon) { this.clickPolygon = clickPolygon; }
    
    private Vector2 interactionLocation;

    public Vector2 getInteractionLocation() { return this.interactionLocation; }
    public void setInteractionLocation(Vector2 interactionLocation) { this.interactionLocation = interactionLocation; }
    
    private boolean interactFromAnywhere;
    
    public boolean getInteractFromAnywhere() { return this.interactFromAnywhere; }
    public void setInteractFromAnywhere(boolean interactFromAnywhere) { this.interactFromAnywhere = interactFromAnywhere; }
    
    private Conversation conversation;
    
    public Conversation getConversation() { return this.conversation; }
    public void setConversation(Conversation conversation) { this.conversation = conversation; }
    
    public Character AddCharacter(Vector2 position)
    {
        Character character = new Character(position);
        this.characterList.add(character);
        return character;
    }
    
    public void CreateVideo() throws IOException
    {
        this.video = new Video(this.videoId);
        this.video.setShouldLoop(true);
        Collections.sort(this.characterList);
        
        double maxX = 0;
        double maxY = 0;
        
        // We'll first get the maximum width needed for our composite images.
        for (Character character : this.characterList)
        {
            BufferedImage image = character.GetFrame(0);
            double x = character.position.getX() + image.getWidth();
            double y = character.position.getY() + image.getHeight();
            
            if (x > maxX)
            {
                maxX = x;
            }
            
            if (y > maxY)
            {
                maxY = y;
            }
        }
        
        // We'll assume that all character animations have the same number of frames -
        // we've got a problem if they don't.
        for (int i = 0; i < this.characterList.get(0).GetFrameCount(); i++)
        {
            BufferedImage compositeImage = new BufferedImage((int)maxX, (int)maxY, BufferedImage.TYPE_4BYTE_ABGR);
            
            Graphics2D newImageGraphics = compositeImage.createGraphics();
            newImageGraphics.setComposite(AlphaComposite.Clear);
            newImageGraphics.fillRect(0, 0, compositeImage.getWidth(), compositeImage.getHeight());
            newImageGraphics.setComposite(AlphaComposite.SrcOver);
            
            for (Character character : this.characterList)
            {
                newImageGraphics.drawImage(character.GetFrame(i), null, (int)character.position.getX(), (int)character.position.getY());
            }
            
            this.video.AddFrame(this.frameDuration, compositeImage);
        }
        
        Case.getInstance().getAnimationManager().AddVideo(this.videoId, this.video);
    }
    
    public void SaveToXml(CaseXmlWriter writer) throws IOException, CaseParseException
    {
        writer.StartElement("Crowd");
        
        writer.WriteTextElement("Name", this.name);
        writer.WriteIntElement("AnchorPosition", this.anchorPosition);
        
        writer.StartElement("Position");
        this.position.SaveToXml(writer);
        writer.EndElement();
        
        writer.StartElement("HitBox");
        this.hitBox.SaveToXml(writer);
        writer.EndElement();
        
        writer.StartElement("ClickPolygon");
        writer.WritePolygonElement(clickPolygon);
        writer.EndElement();
        
        if (this.interactionLocation != null)
        {
            writer.StartElement("InteractionLocation");
            this.interactionLocation.SaveToXml(writer);
            writer.EndElement();
        }
            
        writer.WriteBooleanElement("InteractFromAnywhere", this.interactFromAnywhere);
        
        writer.StartElement("Encounter");
        Encounter encounter = new Encounter();
        encounter.setOneShotConversation(this.conversation);
        encounter.SaveToXml(writer);
        writer.EndElement();
        
        writer.WriteTextElement("VideoId", this.videoId);
        
        writer.EndElement();
    }

    public void markChildSprites(String markParent)
    {
        this.video.markChildSprites(markParent);
    }
    
    public class Character implements Comparable<Character>
    {
        private Vector2 position;
        private ArrayList<String> frameFilePathList;
        private ArrayList<BufferedImage> frameBufferedImageList;
        
        public Character(Vector2 position)
        {
            this.position = position;
            this.frameFilePathList = new ArrayList<String>();
            this.frameBufferedImageList = new ArrayList<BufferedImage>();
        }
        
        public void AddFrameFilePath(String filePath)
        {
            this.frameFilePathList.add(filePath);
        }
        
        public int GetFrameCount()
        {
            return this.frameFilePathList.size();
        }
        
        public BufferedImage GetFrame(int index) throws IOException
        {
            if (this.frameBufferedImageList.isEmpty())
            {
                for (String filePath : this.frameFilePathList)
                {
                    try
                    {
                        this.frameBufferedImageList.add(ImageIO.read(new File("data/" + filePath)));
                    }
                    catch (IOException e)
                    {
                        throw new IOException("Couldn't find file: data/" + filePath);
                    }
                }
            }
            
            return this.frameBufferedImageList.get(index);
        }

        public int compareTo(Character other)
        {
            return (int)(this.position.getY() - other.position.getY());
        }
    }
}
